package com.skyline.wo.controller;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.imagine.core.ImageInfo;
import net.imagine.core.ImageOperations;
import net.imagine.core.ResultState;
import net.imagine.core.engine.ImageEngine;
import net.imagine.provider.skyline.ImageCropResult;
import net.imagine.provider.skyline.ImageCutter;
import net.imagine.provider.skyline.ImageResizeResult;
import net.imagine.provider.skyline.MultipartImageResizeTask;
import net.imagine.provider.skyline.SkylineImageCropTask;
import net.imagine.provider.skyline.SkylineImagineUtils;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.jackson.JsonEncoding;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import com.skyline.attraction.model.Attraction;
import com.skyline.attraction.service.AttractionService;
import com.skyline.base.exception.NotLoginException;
import com.skyline.base.exception.OperateFailedException;
import com.skyline.common.util.Constant;
import com.skyline.common.util.WebHelper;
import com.skyline.user.model.User;
import com.skyline.user.service.PersonalInfoService;
import com.skyline.wo.model.Album;
import com.skyline.wo.model.Photo;
import com.skyline.wo.service.AlbumService;

/**
 * SWF批量文件上传用到的上传类，不能使用SpringMVC,会返回406<br/>
 * 必须由Spring来管理，且beanId必须为uploadAction<br/>
 * 具体请参见imagine框架
 * 
 * @author wuqh
 * */
@Component("uploadAction")
public class UploadController {
	private static final Log LOGGER = LogFactory.getLog(UploadController.class);
	private static final String URL_SUFFIX = ".html";
	private ObjectMapper objectMapper = new ObjectMapper();

	@Value("${imagine.localStorePath}")
	private String basePath;
	@Value("${album.uploadphoto.filesize}")
	private long maxSizePerFile;

	@Autowired
	private ImageEngine imagine;
	@Value("${imagine.baseSize1},${imagine.baseSize2}")
	private int[] baseSizes;
	@Autowired
	private ImageOperations operations;
	@Autowired
	private ImageCutter crop;
	@Value("${imagine.protraitSize}")
	private int portraitSize;

	@Autowired
	private PersonalInfoService personalInfoService;
	@Autowired
	private AttractionService attractionService;
	@Autowired
	private AlbumService albumService;

	/**
	 * 批量图片上传（SWF上传），返回图片存放的本地地址
	 * 
	 * @param request
	 * @param response
	 * @throws IOException
	 */
	public void batchUpload(HttpServletRequest request, HttpServletResponse response) throws IOException {
		String albumIdStr = request.getParameter("albumid");
		String userIdStr = request.getParameter("userid");
		if (!"POST".equals(request.getMethod())) {
			throw new OperateFailedException("非法请求");
			//response.sendRedirect(buildRecirectPath(request, "/album/bupload/" + albumIdStr, null));
		}
		long userId = Long.parseLong(userIdStr);
		long albumId = Long.parseLong(albumIdStr);
		MultipartFile file = ((MultipartHttpServletRequest) request).getFile("file");
		// 本地存放路径生成
		if (file == null) {
			outputResultState(response, ResultState.NO_FILE_EXIST, null);
			return;
		}
		File localFile = generateLocalFile(userId, albumId, file.getOriginalFilename());
		// 保存文件
		ResultState result = storeFileInLocal(localFile, file);
		// 输出结果
		outputResultState(response, result, localFile.getAbsolutePath());
	}

	/**
	 * 删除上传成功的图片，此时上传的图片还未写入数据库
	 * 
	 * @param request
	 * @param response
	 * @throws IOException
	 */
	public void delUploaded(HttpServletRequest request, HttpServletResponse response) throws IOException {
		String filePath = request.getParameter("photo_file");
		if ("GET".equals(request.getMethod())) {
			filePath = URLDecoder.decode(filePath, "UTF-8");
		}
		File file = null;
		if (StringUtils.hasLength(filePath)) {
			file = new File(filePath);
		}
		if (file == null || !file.exists()) {
			outputMessage(response, false, "文件不存在");
			return;
		}
		try {
			FileUtils.forceDelete(file);
		} catch (IOException e) {
			LOGGER.warn("删除图片文件失败", e);
			outputMessage(response, false, "删除失败");
			return;
		}
		outputMessage(response, true, "成功");
	}

	/**
	 * 头像上传， 此功能必须和应用位于同一个session中
	 * 
	 * @param request
	 * @param response
	 * @throws IOException
	 */
	public void uploadPortrait(HttpServletRequest request, HttpServletResponse response) throws IOException {
		if (!"POST".equals(request.getMethod())) {
			response.sendRedirect(buildRecirectPath(request, "/user/photoupload", null));
		}

		Map<String, Object> jsonMap = null;

		try {
			User user = checkLoginedUser(request);
			long userId = user.getId();
			Album album = albumService.getUserPortraitAlbum(userId);

			MultipartFile file = ((MultipartHttpServletRequest) request).getFile("file");
			jsonMap = uploadPortraitTemplate(file, album, user);
			if ((Boolean) jsonMap.get("success")) {
				String portraitFile = (String) jsonMap.remove("portraitFile");
				if (StringUtils.hasLength(portraitFile)) {
					personalInfoService.changeUserPortrait(userId, portraitFile);
					user.setPortrait(portraitFile);
					WebHelper.setSessionAttribute(request, Constant.SESSION_USER, user);
				}

			}
			outputJsonResult(response, jsonMap);
		} catch (NotLoginException e) {
			LOGGER.warn("上传头像失败，必须登录", e);
			outputMessage(response, false, "上传头像必须登录", false);
		} catch (Exception e) {
			LOGGER.warn("上传头像失败", e);
			outputMessage(response, false, "上传头像失败，请稍候重试", true);
		}
		
	}

	/**
	 * 景点头像上传， 此功能必须和应用位于同一个session中
	 * 
	 * @param request
	 * @param response
	 * @throws IOException
	 */
	public void uploadAttractionPortrait(HttpServletRequest request, HttpServletResponse response) throws IOException {
		String attractionIdStr = request.getParameter("attractionId");
		String albumIdStr = request.getParameter("albumId");
		if (!"POST".equals(request.getMethod())) {
			response.sendRedirect(buildRecirectPath(request, "/attraction/upPortrait/"+attractionIdStr, null));
		}

		Map<String, Object> jsonMap = null;

		try {
			User user = checkLoginedUser(request);

			long attractionId = Long.parseLong(attractionIdStr);
			long albumId = Long.parseLong(albumIdStr);
			Album album = albumService.getAlbumById(albumId);
			Attraction attraction = attractionService.getAttractionForChange(attractionId, user.getId());
			/**
			 * 构造一个attraction的user
			 */
			User tempuser = new User();
			tempuser.setId(attraction.getId());
			tempuser.setNickname(attraction.getName());
			tempuser.setPortrait(attraction.getPortrait());

			MultipartFile file = ((MultipartHttpServletRequest) request).getFile("file");
			jsonMap = uploadPortraitTemplate(file, album, tempuser);
			if ((Boolean) jsonMap.get("success")) {
				String portraitFile = (String) jsonMap.get("portraitFile");
				if (StringUtils.hasLength(portraitFile)) {
					attractionService.updateAttractionPortrait(attractionId, portraitFile);
					attraction.setPortrait(portraitFile);
				}
			}
			outputJsonResult(response, jsonMap);
		} catch (NotLoginException e) {
			LOGGER.warn("上传头像失败，必须登录", e);
			outputMessage(response, false, "修改照片描述必须登录", false);
		} catch (Exception e) {
			LOGGER.warn("上传头像失败", e);
			outputMessage(response, false, "上传头像失败，请稍候重试", true);
		}
	}

	/**
	 * 上传头像方法模板（抽取头像相册类似部分重构）
	 * 
	 * @param file
	 *            request中的文件（MultipartFile对象）
	 * @param album
	 *            头像相册（需要包含Id,albumName,activity）
	 * @param user
	 *            头像相册所属对象（需要包含id,nickname,portrait）
	 * @return
	 * @throws IOException
	 */
	private Map<String, Object> uploadPortraitTemplate(MultipartFile file, Album album, User user) throws IOException {
		Map<String, Object> jsonMap = null;
		long userId = user.getId();
		long albumId = album.getId();

		if (file == null) {
			jsonMap = resultStateToJsonMap(ResultState.NO_FILE_EXIST, null);
			jsonMap.put("logined", Boolean.TRUE);
			return jsonMap;
		}

		ImageResizeResult processResult = resizeMultipartFile(file, albumId, userId);

		ResultState resultState = processResult.getResultState();
		if (resultState != ResultState.SUCCESS) {
			jsonMap = resultStateToJsonMap(resultState, null);
			jsonMap.put("logined", Boolean.TRUE);
			return jsonMap;
		}
		Photo portrait = albumService.createPortraitPhoto(user, album, processResult);

		String remoteFileKey = getPortraitRemoteFileKey(portrait);
		ImageCropResult cropResult = cropRemoteFile(remoteFileKey, albumId, userId);
		String portraitFile = cropResult.getFileKey();

		jsonMap = toJsonMap(true, "上传头像成功", null, true);
		jsonMap.put("fileKey", remoteFileKey);
		jsonMap.put("portraitFile", portraitFile); // 返回供各方法更新数据库
		return jsonMap;
	}

	private ImageResizeResult resizeMultipartFile(MultipartFile file, long albumId, long userId) {
		MultipartImageResizeTask resizeTask = new MultipartImageResizeTask(file, baseSizes, null, false);
		resizeTask.setAlbumId(albumId);
		resizeTask.setUserId(userId);
		resizeTask.setSupportMulitFrame(false);
		ImageResizeResult processResult = imagine.processSingleImage(basePath, resizeTask);
		return processResult;
	}

	private ImageCropResult cropRemoteFile(String remoteFileKey, long albumId, long userId) throws IOException {
		SkylineImageCropTask cropTask = new SkylineImageCropTask(remoteFileKey, portraitSize);
		cropTask.setAlbumId(albumId);
		cropTask.setUserId(userId);
		ImageCropResult cropResult = crop.processImage(basePath, cropTask);
		return cropResult;
	}

	private String getPortraitRemoteFileKey(Photo portrait) {
		String smallFile = portrait.getSmallFile();
		String ext = portrait.getExt();
		return smallFile + '.' + ext;
	}

	protected void outputMessage(HttpServletResponse response, boolean isSuccess, String errMsg) throws JsonGenerationException, JsonMappingException, IOException {
		Map<String, Object> jsonMap = toJsonMap(isSuccess, errMsg, null);
		outputJsonResult(response, jsonMap);
	}
	
	protected void outputMessage(HttpServletResponse response, boolean isSuccess, String errMsg, boolean logined) throws JsonGenerationException, JsonMappingException, IOException {
		Map<String, Object> jsonMap = toJsonMap(isSuccess, errMsg, null, logined);
		outputJsonResult(response, jsonMap);
	}
	
	protected void outputJsonResult(HttpServletResponse response, Map<String, Object> jsonMap)
			throws JsonGenerationException, JsonMappingException, IOException {
		OutputStream out = response.getOutputStream();
		outputJsonResult(out, jsonMap);
	}

	protected void outputJsonResult(OutputStream out, Map<String, Object> jsonMap) throws JsonGenerationException,
			JsonMappingException, IOException {
		JsonGenerator jsonGenerator = objectMapper.getJsonFactory().createJsonGenerator(out, JsonEncoding.UTF8);
		objectMapper.writeValue(jsonGenerator, jsonMap);
	}

	protected void outputResultState(HttpServletResponse response, ResultState result, String filePath) throws JsonGenerationException, JsonMappingException, IOException {
		Map<String, Object> jsonMap = resultStateToJsonMap(result, filePath);
		outputJsonResult(response, jsonMap);
	}
	
	protected Map<String, Object> resultStateToJsonMap(ResultState result, String filePath) {
		switch (result) {
		case SUCCESS:
			return toJsonMap(true, "成功", filePath);
		case FILE_TO_LARGE:
			return toJsonMap(false, "文件过大", filePath);
		case STORE_FAILED:
			return toJsonMap(false, "保存失败", filePath);
		case NOT_IMAGE:
			return toJsonMap(false, "不是图片", filePath);
		case EXCEED_MAX_CAPACITY:
			return toJsonMap(false, "超过容量", filePath);
		case UNKOWN_FAILED:
			return toJsonMap(false, "未知错误", filePath);
		default:
			return toJsonMap(false, "未知错误", filePath);
		}
	}

	protected Map<String, Object> toJsonMap(boolean isSuccess, String errMsg, String filePath) {
		Map<String, Object> jsonMap = new HashMap<String, Object>();
		jsonMap.put("success", Boolean.valueOf(isSuccess));
		jsonMap.put("errmsg", errMsg);
		if (isSuccess && StringUtils.hasLength(filePath)) {
			jsonMap.put("file", filePath);
		}
		return jsonMap;
	}

	protected Map<String, Object> toJsonMap(boolean isSuccess, String errMsg, String filePath, boolean logined) {
		Map<String, Object> jsonMap = toJsonMap(isSuccess, errMsg, filePath);
		jsonMap.put("logined", Boolean.valueOf(logined));
		return jsonMap;
	}

	private File generateLocalFile(long userId, long albumId, String oriFilename) {
		String baseName = FilenameUtils.getBaseName(oriFilename);
		String ext = FilenameUtils.getExtension(oriFilename);
		String filePath = SkylineImagineUtils.generateFilePath(basePath, userId, albumId, oriFilename);
		File localFile = new File(filePath);
		int num = 0;
		while (localFile.exists()) {// 处理同名文件
			StringBuffer tempFilename = new StringBuffer(baseName);
			tempFilename.append('(').append(num).append(')').append('.').append(ext);
			filePath = SkylineImagineUtils.generateFilePath(basePath, userId, albumId, tempFilename.toString());
			localFile = new File(filePath);
			num++;
		}
		return localFile;
	}

	private ResultState storeFileInLocal(File localFile, MultipartFile file) {
		// 判断文件是否过大
		long actualSize = file.getSize();
		if (actualSize > maxSizePerFile) {
			return ResultState.FILE_TO_LARGE;
		}
		// TODO 判断是否到达容量上限

		// 保存到本地文件
		try {
			FileOutputStream out = FileUtils.openOutputStream(localFile);
			IOUtils.copy(file.getInputStream(), out);
		} catch (IOException e) {
			LOGGER.warn("写入本地临时文件失败", e);
			return ResultState.STORE_FAILED;
		}

		// 判断文件是否为图片格式
		ImageInfo info = operations.showImageInfo(localFile.getAbsolutePath());
		if (info == null) {
			try {
				FileUtils.forceDelete(localFile);
			} catch (IOException e) {
				LOGGER.warn("删除非图片文件失败", e);
			}

			return ResultState.NOT_IMAGE;
		}
		return ResultState.SUCCESS;
	}

	protected String buildRecirectPath(HttpServletRequest request, String baseUrl, String paramStr) {
		if (StringUtils.hasLength(paramStr)) {
			return request.getContextPath() + baseUrl + URL_SUFFIX + "?" + paramStr;
		} else {
			return request.getContextPath() + baseUrl + URL_SUFFIX;
		}

	}

	protected User checkLoginedUser(HttpServletRequest request) {
		User user = WebHelper.getSessionAttribute(request, Constant.SESSION_USER);
		if (user == null) {
			throw new NotLoginException();
		} else {
			return user;
		}
	}
}
